﻿#region << 版 本 注 释 >>
/*----------------------------------------------------------------
 * 版权所有 (c) 2022 北京超维景生物科技有限公司 保留所有权利。
 * CLR版本：4.0.30319.42000
 * 文件名：Kernel
 * 
 * 创建者：huangyang
 * 电子邮箱：huangyang@tvscope.cn
 * 创建时间：2023/2/10 15:13:50
 * 版本：V1.0.0
 * 描述：
 *
 * ----------------------------------------------------------------
 * 修改人：
 * 时间：
 * 修改说明：
 *
 * 版本：V1.0.1
 *----------------------------------------------------------------*/
#endregion << 版 本 注 释 >>

using System;
using System.Collections.Generic;
using System.Drawing.Imaging;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using ImageK.Java;

namespace ImageK.Util
{
    public class ImageUtil
    {
        /// <summary>
        /// 从字节数组创建8bit灰度图像
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <returns></returns>
        public static Bitmap BuildByBytes(byte[] bytes, int width, int height, ColorModel cm)
        {
            return ImageUtil.BuiltGrayBitmap(bytes, width, height);
            // IntPtr pData = Marshal.AllocHGlobal(width * height);
            // Marshal.Copy(bytes, 0, pData, width * height);
            // //stribe表示指定相邻扫描行开始处之间字节偏移量的整数。这通常是以像素的位数乘以位图的宽度。传递给此参数的值必须为4 的倍数（向上舍入为四字节边界）。
            // //format值为Format8bppIndexed，表示创建每像素为8位的索引图像，颜色表中有256中颜色；
            // //由于Format8bppIndexed表示创建的是索引图像，所以我们需要修改调色板，为图像中每一位数据提供索引。
            // Bitmap bmp = new Bitmap(width, height, GetStride(width), PixelFormat.Format8bppIndexed, pData);
            //
            // ColorPalette palette = bmp.Palette;
            // // for (int i = 0; i < 256; i++)
            // // {
            // //     palette.Entries[i] = Color.FromArgb(i, i, i);
            // // }
            // cm.UpdatePalette(palette);
            // bmp.Palette = palette;
            //
            // return bmp;
        }

        /// <summary>
        /// 向上舍入为四字节边界
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        private static int GetStride(int value)
        {
            var mod = value % 4;
            if (mod == 0)
                return value;
            else
            {
                return value + 4 - mod;
            }
        }

        /// <summary>
        /// 从字节数组创建8bit彩色图像
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <returns></returns>
        public static Bitmap BuildColorByBytes(byte[] bytes, int width, int height)
        {
            // IntPtr pData = Marshal.AllocHGlobal(width * height * 3);
            // Marshal.Copy(bytes, 0, pData, width * height * 3);
            // Bitmap bmp = new Bitmap(width, height, GetStride(3* width), PixelFormat.Format24bppRgb, pData);
            // //创建24位彩色图像时，由于每个像素的三个分量R、G、B已经表示了当前像素的颜色，所以不需要调色板。
            //
            // return bmp;

            return BuiltColorBitmap(bytes, width, height);
        }

        public static byte[] GetRGBBytes(int[] pixels)
        {
            if (pixels.Length == 0)
            {
                return new byte[0];
            }
            int c, r, g, b;
            var data = new byte[pixels.Length * 3];
            for (int i = 0; i < pixels.Length; i++)
            {
                c = pixels[i];
                r = (c & 0xff0000) >> 16;
                g = (c & 0xff00) >> 8;
                b = c & 0xff;
                data[i * 3] = (byte)r;
                data[i * 3 + 1] = (byte)g;
                data[i * 3 + 2] = (byte)b;
            }

            return data;
        }


        public static byte[] GetBGRBytes(int[] pixels)
        {
            if (pixels.Length == 0)
            {
                return new byte[0];
            }
            int c, r, g, b;
            var data = new byte[pixels.Length * 3];
            for (int i = 0; i < pixels.Length; i++)
            {
                c = pixels[i];
                r = (c & 0xff0000) >> 16;
                g = (c & 0xff00) >> 8;
                b = c & 0xff;
                data[i * 3] = (byte)b;
                data[i * 3 + 1] = (byte)g;
                data[i * 3 + 2] = (byte)r;
            }

            return data;
        }

        public static byte[] GetBGRBytes(byte[] pixels, ColorModel cm)
        {
            if (pixels.Length == 0)
            {
                return new byte[0];
            }
            int c, r, g, b;
            var data = new byte[pixels.Length * 3];
            for (int i = 0; i < pixels.Length; i++)
            {
                c = pixels[i];
                r = cm.GetR((byte)c);
                g = cm.GetG((byte)c);
                b = cm.GetB((byte)c);
                data[i * 3] = (byte)b;
                data[i * 3 + 1] = (byte)g;
                data[i * 3 + 2] = (byte)r;
            }

            return data;
        }

        /// <summary>
        /// 取源图像R通道数据，并转化为8位灰度图像。
        /// </summary>
        /// <param name="original"> 源图像。 </param>
        /// <returns> 8位灰度图像。 </returns>
        public static Bitmap RgbToR8(Bitmap original)
        {
            if (original != null)
            {
                // 将源图像内存区域锁定
                Rectangle rect = new Rectangle(0, 0, original.Width, original.Height);
                BitmapData bmpData = original.LockBits(rect, ImageLockMode.ReadOnly,
                        PixelFormat.Format24bppRgb);

                // 获取图像参数
                int width = bmpData.Width;
                int height = bmpData.Height;
                int stride = bmpData.Stride;  // 扫描线的宽度,比实际图片要大
                int offset = stride - width * 3;  // 显示宽度与扫描线宽度的间隙
                IntPtr ptr = bmpData.Scan0;   // 获取bmpData的内存起始位置的指针                
                int scanBytesLength = stride * height;  // 用stride宽度，表示这是内存区域的大小

                // 分别设置两个位置指针，指向源数组和目标数组
                int posScan = 0, posDst = 0;
                byte[] rgbValues = new byte[scanBytesLength];  // 为目标数组分配内存
                Marshal.Copy(ptr, rgbValues, 0, scanBytesLength);  // 将图像数据拷贝到rgbValues中
                // 分配灰度数组
                byte[] grayValues = new byte[width * height]; // 不含未用空间。
                                                              // 计算灰度数组
                byte blue, green, red;

                for (int i = 0; i < height; i++)
                {
                    for (int j = 0; j < width; j++)
                    {

                        blue = rgbValues[posScan];
                        green = rgbValues[posScan + 1];
                        red = rgbValues[posScan + 2];
                        grayValues[posDst] = red;
                        posScan += 3;
                        posDst++;

                    }
                    // 跳过图像数据每行未用空间的字节，length = stride - width * bytePerPixel
                    posScan += offset;
                }

                // 内存解锁
                Marshal.Copy(rgbValues, 0, ptr, scanBytesLength);
                original.UnlockBits(bmpData);  // 解锁内存区域

                // 构建8位灰度位图
                Bitmap retBitmap = BuiltGrayBitmap(grayValues, width, height);
                return retBitmap;
            }
            else
            {
                return null;
            }
        }
        /// <summary>
        /// 用灰度数组新建一个8位灰度图像。
        /// </summary>
        /// <param name="rawValues"> 灰度数组(length = width * height)。 </param>
        /// <param name="width"> 图像宽度。 </param>
        /// <param name="height"> 图像高度。 </param>
        /// <returns> 新建的8位灰度位图。 </returns>
        private static Bitmap BuiltGrayBitmap(byte[] rawValues, int width, int height)
        {
            // 新建一个8位灰度位图，并锁定内存区域操作
            Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
            BitmapData bmpData = bitmap.LockBits(new Rectangle(0, 0, width, height),
                 ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);

            // 计算图像参数
            int offset = bmpData.Stride - bmpData.Width;        // 计算每行未用空间字节数
            IntPtr ptr = bmpData.Scan0;                         // 获取首地址
            int scanBytes = bmpData.Stride * bmpData.Height;    // 图像字节数 = 扫描字节数 * 高度
            byte[] grayValues = new byte[scanBytes];            // 为图像数据分配内存

            // 为图像数据赋值
            int posSrc = 0, posScan = 0;                        // rawValues和grayValues的索引
            for (int i = 0; i < height; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    grayValues[posScan++] = rawValues[posSrc++];
                }
                // 跳过图像数据每行未用空间的字节，length = stride - width * bytePerPixel
                posScan += offset;
            }

            // 内存解锁
            Marshal.Copy(grayValues, 0, ptr, scanBytes);
            bitmap.UnlockBits(bmpData);  // 解锁内存区域

            // 修改生成位图的索引表，从伪彩修改为灰度
            ColorPalette palette;
            // 获取一个Format8bppIndexed格式图像的Palette对象
            using (Bitmap bmp = new Bitmap(1, 1, PixelFormat.Format8bppIndexed))
            {
                palette = bmp.Palette;
            }
            for (int i = 0; i < 256; i++)
            {
                palette.Entries[i] = Color.FromArgb(i, i, i);
            }
            // 修改生成位图的索引表
            bitmap.Palette = palette;

            return bitmap;
        }

        private static Bitmap BuiltColorBitmap(byte[] rawValues, int width, int height)
        {
            // 新建一个24位位图，并锁定内存区域操作
            Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb);
            BitmapData bmpData = bitmap.LockBits(new Rectangle(0, 0, width, height),
                ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);

            // 计算图像参数
            int offset = bmpData.Stride - bmpData.Width * 3;        // 计算每行未用空间字节数
            IntPtr ptr = bmpData.Scan0;                         // 获取首地址
            int scanBytes = bmpData.Stride * bmpData.Height;    // 图像字节数 = 扫描字节数 * 高度
            byte[] colorValues = new byte[scanBytes];            // 为图像数据分配内存

            // 为图像数据赋值
            int posSrc = 0, posScan = 0;                        // rawValues和grayValues的索引
            for (int i = 0; i < height; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    colorValues[posScan++] = rawValues[posSrc++];
                    colorValues[posScan++] = rawValues[posSrc++];
                    colorValues[posScan++] = rawValues[posSrc++];
                }
                // 跳过图像数据每行未用空间的字节，length = stride - width * bytePerPixel
                posScan += offset;
            }

            // 内存解锁
            Marshal.Copy(colorValues, 0, ptr, scanBytes);
            bitmap.UnlockBits(bmpData);  // 解锁内存区域

            // 修改生成位图的索引表，从伪彩修改为灰度
            ColorPalette palette;
            // 获取一个Format8bppIndexed格式图像的Palette对象
            using (Bitmap bmp = new Bitmap(1, 1, PixelFormat.Format8bppIndexed))
            {
                palette = bmp.Palette;
            }
            for (int i = 0; i < 256; i++)
            {
                palette.Entries[i] = Color.FromArgb(i, i, i);
            }
            // 修改生成位图的索引表
            bitmap.Palette = palette;

            return bitmap;
        }
    }
}
